﻿using ExpressionLinq;
using System.Reflection;

namespace System.Linq.Expressions
{
    public class WhereExpressionVisitor : ExpressionVisitor
    {
        private Stack<string> _stack = new Stack<string>();
        private List<DataMember> _dataMemberList = new List<DataMember>();
        private string _prefix = "[";
        private string _suffix = "]";

        public string ToSql()
        {
            var sql = string.Concat(_stack);
            _stack.Clear();
            return sql;
        }

        public override Expression Visit(Expression node)
        {
            return base.Visit(node);
        }

        protected override Expression VisitMemberInit(MemberInitExpression node)
        {
            return node;
        }

        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node == null) throw new ArgumentNullException("BinaryExpression");
            _stack.Push(")");
            base.Visit(node.Right);
            _stack.Push(ToSqlOperator(node.NodeType));
            base.Visit(node.Left);
            _stack.Push("(");
            return node;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (node == null) throw new ArgumentNullException("MemberExpression");
            switch (node.Expression!.NodeType)
            {
                case ExpressionType.Parameter:
                    _stack.Push($" {_prefix}{node.Member.Name}{_suffix} ");
                    break;
                case ExpressionType.MemberAccess:
                    base.Visit(node.Expression!);
                    break;
                default:
                    {
                        base.Visit(node.Expression);
                        //Type type = node.Member.ReflectedType;
                        //PropertyInfo propertyInfo = type.GetProperty(node.Member.Name, BindingFlags.Instance | BindingFlags.Public);
                        //object o;
                        //if (propertyInfo != null)
                        //{
                        //    o = propertyInfo.GetValue(null);
                        //}
                        //else
                        //{
                        //    FieldInfo field = type.GetField(node.Member.Name, BindingFlags.Static | BindingFlags.Public);
                        //    o = field.GetValue(null);
                        //}
                    }
                    break;
            }

            return node;
        }

        protected override Expression VisitConstant(ConstantExpression node)
        {
            if (node == null) throw new ArgumentNullException("ConstantExpression");
            _stack.Push($" {node.Value ?? ""} ");
            return node;
        }

        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            if (node == null) throw new ArgumentNullException("MethodCallExpression");
            var format = node.Method.Name switch
            {
                "StartsWith" => "{0} LIKE '{1}%'",
                "EndWith" => "{0} LIKE '%{1}'",
                "Contains" => "{0} LIKE '%{1}%'",
                _ => throw new NotSupportedException($"{node.Method.Name} is not supported"),
            };
            base.Visit(node.Object);
            base.Visit(node.Arguments[0]);
            var right = _stack.Pop();
            var left = _stack.Pop();
            _stack.Push(string.Format(format, left, right.Trim()));
            return node;
        }

        #region private methods
        private string ToSqlOperator(ExpressionType expressionType)
        {
            switch (expressionType)
            {
                case ExpressionType.Equal:
                    return " = ";
                case ExpressionType.NotEqual:
                    return " <> ";
                case ExpressionType.GreaterThan:
                    return " > ";
                case ExpressionType.GreaterThanOrEqual:
                    return " >= ";
                case ExpressionType.LessThan:
                    return " < ";
                case ExpressionType.LessThanOrEqual:
                    return " <= ";
                case ExpressionType.Add:
                    return " + ";
                case ExpressionType.Multiply:
                    return " * ";
                case ExpressionType.Divide:
                    return " / ";
                case ExpressionType.Modulo:
                    return " % ";
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                    return " AND ";
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    return " OR ";
                case ExpressionType.Assign:
                    return " = ";
                default:
                    return " ";
            }
        }

        private void SetDataMember(Expression node)
        {
            string name;
            Type type;
            string reflectName;
            Type? reflectType;
            if (node is  MemberExpression memberExpression)
            {
                name = memberExpression.Member.Name;
                type = memberExpression.Type;
                reflectType = memberExpression.Member.ReflectedType;
                if (reflectType != null)
                    reflectName = (memberExpression.Expression as MemberExpression)!.Member.Name;

            }
            else
            {
                var constExpression = node as ConstantExpression;
                if (constExpression != null)
                {
                    type = constExpression.Type;
                    
                }
            }
            
            //if (_dataMemberList.Any(m => m.Name == node.Member.Name && m.Type == node.Type && m.ReflectName == node.Expression.me))
        }
        #endregion
    }
}
